package es

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/elastic/go-elasticsearch/v8"
)

// ESClient 封装了 elasticsearch 客户端
type ESClient struct {
	*elasticsearch.Client
	timeout time.Duration
	index   string // 默认索引名称
}

// NewESClient 创建新的 Elasticsearch 客户端
func NewESClient(addresses []string, apiKey string, opts ...Option) (*ESClient, error) {
	cfg := elasticsearch.Config{
		Addresses: addresses,
		APIKey:    apiKey,
	}

	client, err := elasticsearch.NewClient(cfg)
	if err != nil {
		return nil, fmt.Errorf("failed to create Elasticsearch client: %w", err)
	}

	esClient := &ESClient{
		Client:  client,
		timeout: 5 * time.Second,
	}

	for _, opt := range opts {
		opt(esClient)
	}

	return esClient, nil
}

// Option 客户端配置选项
type Option func(*ESClient)

// WithTimeout 设置请求超时时间
func WithTimeout(timeout time.Duration) Option {
	return func(c *ESClient) {
		c.timeout = timeout
	}
}

// WithDefaultIndex 设置默认索引名称
func WithDefaultIndex(index string) Option {
	return func(c *ESClient) {
		c.index = index
	}
}

// IndexExists 检查索引是否存在
func (c *ESClient) IndexExists(ctx context.Context, index string) (bool, error) {
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	res, err := c.Indices.Exists(
		[]string{index},
		c.Indices.Exists.WithContext(ctx),
	)
	if err != nil {
		return false, fmt.Errorf("failed to check index existence: %w", err)
	}
	defer res.Body.Close()

	if res.StatusCode == 200 {
		return true, nil
	} else if res.StatusCode == 404 {
		return false, nil
	}

	return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
}

// CreateIndexIfNotExists 如果索引不存在则创建
func (c *ESClient) CreateIndexIfNotExists(ctx context.Context, index string, mapping string) error {
	exists, err := c.IndexExists(ctx, index)
	if err != nil {
		return fmt.Errorf("failed to check index existence: %w", err)
	}
	if exists {
		return nil
	}

	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	res, err := c.Indices.Create(
		index,
		c.Indices.Create.WithContext(ctx),
		c.Indices.Create.WithBody(strings.NewReader(mapping)),
	)
	if err != nil {
		return fmt.Errorf("failed to create index: %w", err)
	}
	defer res.Body.Close()

	if res.IsError() {
		return fmt.Errorf("index creation error: %s", res.String())
	}

	return nil
}

// Ping 检查 Elasticsearch 连接
func (c *ESClient) Ping(ctx context.Context) error {
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	res, err := c.Info(c.Info.WithContext(ctx))
	if err != nil {
		return fmt.Errorf("failed to ping Elasticsearch: %w", err)
	}
	defer res.Body.Close()

	if res.IsError() {
		return fmt.Errorf("elasticsearch ping error: %s", res.String())
	}

	return nil
}

// IndexDocument 索引文档
func (c *ESClient) IndexDocument(ctx context.Context, index string, id string, document interface{}) error {
	if index == "" {
		index = c.index
		if index == "" {
			return fmt.Errorf("no index specified")
		}
	}

	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	docJSON, err := json.Marshal(document)

	if err != nil {
		return fmt.Errorf("failed to marshal document: %w", err)
	}

	res, err := c.Index(
		index,
		strings.NewReader(string(docJSON)),
		c.Index.WithContext(ctx),
		c.Index.WithDocumentID(id),
		c.Index.WithRefresh("true"),
	)
	fmt.Printf("res: %v", res)
	if err != nil {
		return fmt.Errorf("failed to index document: %w", err)
	}
	defer res.Body.Close()

	if res.IsError() {
		return fmt.Errorf("document indexing error: %s", res.String())
	}

	return nil
}
